//  
#include "MathStdafx.h"
#include "LsMath.h"
#include "asm_math.h"
#include "Vector3.h"
#include "math/Vector2.h"
#include "limits"
namespace Apo
{

    const float LsMath::POS_INFINITY = std::numeric_limits<float>::infinity();
    const float LsMath::NEG_INFINITY = -std::numeric_limits<float>::infinity();
    const float LsMath::PI = float( 4.0 * atan( 1.0 ) );
    const float LsMath::TWO_PI = float( 2.0 * PI );
    const float LsMath::HALF_PI = float( 0.5 * PI );
	const float LsMath::fDeg2Rad = PI / float(180.0);
	const float LsMath::fRad2Deg = float(180.0) / PI;
	const float LsMath::LOG2 = log(float(2.0));

    int LsMath::mTrigTableSize;
   LsMath::AngleUnit LsMath::msAngleUnit;

    float  LsMath::mTrigTableFactor;
    float *LsMath::mSinTable = NULL;
    float *LsMath::mTanTable = NULL;

    //-----------------------------------------------------------------------
    LsMath::LsMath( unsigned int trigTableSize )
    {
        msAngleUnit = AU_DEGREE;

        mTrigTableSize = trigTableSize;
        mTrigTableFactor = mTrigTableSize / LsMath::TWO_PI;

//         mSinTable = _ALLOC_T(float, mTrigTableSize, MEMCATEGORY_GENERAL);
//         mTanTable = _ALLOC_T(float, mTrigTableSize, MEMCATEGORY_GENERAL);

        buildTrigTables();
    }

    //-----------------------------------------------------------------------
    LsMath::~LsMath()
    {
//         _FREE(mSinTable, MEMCATEGORY_GENERAL);
//         _FREE(mTanTable, MEMCATEGORY_GENERAL);
    }

    //-----------------------------------------------------------------------
    void LsMath::buildTrigTables(void)
    {
        // Build trig lookup tables
        // Could get away with building only PI sized Sin table but simpler this 
        // way. Who cares, it'll ony use an extra 8k of memory anyway and I like 
        // simplicity.
        float angle;
        for (int i = 0; i < mTrigTableSize; ++i)
        {
            angle = LsMath::TWO_PI * i / mTrigTableSize;
            mSinTable[i] = sin(angle);
            mTanTable[i] = tan(angle);
        }
    }
	//-----------------------------------------------------------------------	
	float LsMath::SinTable (float fValue)
    {
        // Convert range to index values, wrap if required
        int idx;
        if (fValue >= 0)
        {
            idx = int(fValue * mTrigTableFactor) % mTrigTableSize;
        }
        else
        {
            idx = mTrigTableSize - (int(-fValue * mTrigTableFactor) % mTrigTableSize) - 1;
        }

        return mSinTable[idx];
    }
	//-----------------------------------------------------------------------
	float LsMath::TanTable (float fValue)
    {
        // Convert range to index values, wrap if required
		int idx = int(fValue *= mTrigTableFactor) % mTrigTableSize;
		return mTanTable[idx];
    }
    //-----------------------------------------------------------------------
    int LsMath::ISign (int iValue)
    {
        return ( iValue > 0 ? +1 : ( iValue < 0 ? -1 : 0 ) );
    }

 
    //-----------------------------------------------------------------------
    float LsMath::Sign (float fValue)
    {
        if ( fValue > 0.0 )
            return 1.0;

        if ( fValue < 0.0 )
            return -1.0;

        return 0.0;
    }
	//-----------------------------------------------------------------------
	float LsMath::InvSqrt(float fValue)
	{
		return float(asm_rsq(fValue));
	}
    //-----------------------------------------------------------------------
    float LsMath::UnitRandom ()
    {
        return asm_rand() / asm_rand_max();
    }
    
    //-----------------------------------------------------------------------
    float LsMath::RangeRandom (float fLow, float fHigh)
    {
        return (fHigh-fLow)*UnitRandom() + fLow;
    }

    //-----------------------------------------------------------------------
    float LsMath::SymmetricRandom ()
    {
		return 2.0f * UnitRandom() - 1.0f;
    }

   //-----------------------------------------------------------------------
    void LsMath::setAngleUnit(LsMath::AngleUnit unit)
   {
       msAngleUnit = unit;
   }
   //-----------------------------------------------------------------------
   LsMath::AngleUnit LsMath::getAngleUnit(void)
   {
       return msAngleUnit;
   }
    //-----------------------------------------------------------------------
    float LsMath::AngleUnitsToRadians(float angleunits)
    {
       if (msAngleUnit == AU_DEGREE)
           return angleunits * fDeg2Rad;
       else
           return angleunits;
    }

    //-----------------------------------------------------------------------
    float LsMath::RadiansToAngleUnits(float radians)
    {
       if (msAngleUnit == AU_DEGREE)
           return radians * fRad2Deg;
       else
           return radians;
    }

    //-----------------------------------------------------------------------
    float LsMath::AngleUnitsToDegrees(float angleunits)
    {
       if (msAngleUnit == AU_RADIAN)
           return angleunits * fRad2Deg;
       else
           return angleunits;
    }

    //-----------------------------------------------------------------------
    float LsMath::DegreesToAngleUnits(float degrees)
    {
       if (msAngleUnit == AU_RADIAN)
           return degrees * fDeg2Rad;
       else
           return degrees;
    }

    //-----------------------------------------------------------------------
	bool LsMath::pointInTri2D(const Vector2& p, const Vector2& a, 
		const Vector2& b, const Vector2& c)
    {
		// Winding must be consistent from all edges for point to be inside
		Vector2 v1, v2;
		float dot[3];
		bool zeroDot[3];

		v1 = b - a;
		v2 = p - a;

		// Note we don't care about normalisation here since sign is all we need
		// It means we don't have to worry about magnitude of cross products either
		dot[0] = v1.crossProduct(v2);
		zeroDot[0] = LsMath::RealEqual(dot[0], 0.0f, 1e-3);


		v1 = c - b;
		v2 = p - b;

		dot[1] = v1.crossProduct(v2);
		zeroDot[1] = LsMath::RealEqual(dot[1], 0.0f, 1e-3);

		// Compare signs (ignore colinear / coincident points)
		if(!zeroDot[0] && !zeroDot[1] 
		&& LsMath::Sign(dot[0]) != LsMath::Sign(dot[1]))
		{
			return false;
		}

		v1 = a - c;
		v2 = p - c;

		dot[2] = v1.crossProduct(v2);
		zeroDot[2] = LsMath::RealEqual(dot[2], 0.0f, 1e-3);
		// Compare signs (ignore colinear / coincident points)
		if((!zeroDot[0] && !zeroDot[2] 
			&& LsMath::Sign(dot[0]) != LsMath::Sign(dot[2])) ||
			(!zeroDot[1] && !zeroDot[2] 
			&& LsMath::Sign(dot[1]) != LsMath::Sign(dot[2])))
		{
			return false;
		}


		return true;
    }
	//-----------------------------------------------------------------------
	bool LsMath::pointInTri3D(const Vector3& p, const Vector3& a, 
		const Vector3& b, const Vector3& c, const Vector3& normal)
	{
        // Winding must be consistent from all edges for point to be inside
		Vector3 v1, v2;
		float dot[3];
		bool zeroDot[3];

        v1 = b - a;
        v2 = p - a;

		// Note we don't care about normalisation here since sign is all we need
		// It means we don't have to worry about magnitude of cross products either
        dot[0] = v1.crossProduct(v2).dotProduct(normal);
		zeroDot[0] = LsMath::RealEqual(dot[0], 0.0f, 1e-3);


        v1 = c - b;
        v2 = p - b;

		dot[1] = v1.crossProduct(v2).dotProduct(normal);
		zeroDot[1] = LsMath::RealEqual(dot[1], 0.0f, 1e-3);

		// Compare signs (ignore colinear / coincident points)
		if(!zeroDot[0] && !zeroDot[1] 
			&& LsMath::Sign(dot[0]) != LsMath::Sign(dot[1]))
		{
            return false;
		}

        v1 = a - c;
        v2 = p - c;

		dot[2] = v1.crossProduct(v2).dotProduct(normal);
		zeroDot[2] = LsMath::RealEqual(dot[2], 0.0f, 1e-3);
		// Compare signs (ignore colinear / coincident points)
		if((!zeroDot[0] && !zeroDot[2] 
			&& LsMath::Sign(dot[0]) != LsMath::Sign(dot[2])) ||
			(!zeroDot[1] && !zeroDot[2] 
			&& LsMath::Sign(dot[1]) != LsMath::Sign(dot[2])))
		{
			return false;
		}


        return true;
	}
    //-----------------------------------------------------------------------
    bool LsMath::RealEqual( float a, float b, float tolerance )
    {
        if (fabs(b-a) <= tolerance)
            return true;
        else
            return false;
    }

    //-----------------------------------------------------------------------

    //-----------------------------------------------------------------------

    //-----------------------------------------------------------------------

    //-----------------------------------------------------------------------
 
    //-----------------------------------------------------------------------

    //-----------------------------------------------------------------------


    //-----------------------------------------------------------------------
    Vector3 LsMath::calculateTangentSpaceVector(
        const Vector3& position1, const Vector3& position2, const Vector3& position3,
        float u1, float v1, float u2, float v2, float u3, float v3)
    {
	    //side0 is the vector along one side of the triangle of vertices passed in, 
	    //and side1 is the vector along another side. Taking the cross product of these returns the normal.
	    Vector3 side0 = position1 - position2;
	    Vector3 side1 = position3 - position1;
	    //Calculate face normal
	    Vector3 normal = side1.crossProduct(side0);
	    normal.normalise();
	    //Now we use a formula to calculate the tangent. 
	    float deltaV0 = v1 - v2;
	    float deltaV1 = v3 - v1;
	    Vector3 tangent = deltaV1 * side0 - deltaV0 * side1;
	    tangent.normalise();
	    //Calculate binormal
	    float deltaU0 = u1 - u2;
	    float deltaU1 = u3 - u1;
	    Vector3 binormal = deltaU1 * side0 - deltaU0 * side1;
	    binormal.normalise();
	    //Now, we take the cross product of the tangents to get a vector which 
	    //should point in the same direction as our normal calculated above. 
	    //If it points in the opposite direction (the dot product between the normals is less than zero), 
	    //then we need to reverse the s and t tangents. 
	    //This is because the triangle has been mirrored when going from tangent space to object space.
	    //reverse tangents if necessary
	    Vector3 tangentCross = tangent.crossProduct(binormal);
	    if (tangentCross.dotProduct(normal) < 0.0f)
	    {
		    tangent = -tangent;
		    binormal = -binormal;
	    }

        return tangent;

    }

 
    //-----------------------------------------------------------------------
    Vector3 LsMath::calculateBasicFaceNormalWithoutNormalize(const Vector3& v1, const Vector3& v2, const Vector3& v3)
    {
        Vector3 normal = (v2 - v1).crossProduct(v3 - v1);
        return normal;
    }
	//-----------------------------------------------------------------------
	float LsMath::gaussianDistribution(float x, float offset, float scale)
	{
		float nom = LsMath::Exp(
			-LsMath::Sqr(x - offset) / (2 * LsMath::Sqr(scale)));
		float denom = scale * LsMath::Sqrt(2 * LsMath::PI);

		return nom / denom;

	}



}
